package com.opener.web.acl.service.impl;

import com.opener.web.acl.dao.AclDao;
import com.opener.web.acl.model.SysFunction;
import com.opener.web.acl.model.UserRole;
import com.opener.web.acl.service.AclService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author baboy
 * @date 01/12/2016
 */
@Service
public class AclServiceImpl implements AclService {
    @Autowired
    protected AclDao aclDao;

    @Override
    public List<SysFunction> getAllFunctions() {
        List<SysFunction> functions = aclDao.getAllFunctions();
        return functions;
    }

    /**
     *
     * @param route 资源里配置的路由
     * @param uri 匹配到客户路由
     * @return
     */
    private boolean isRouteMatch(String route,String uri){
        //如果都为空，则说明匹配上了
        if (StringUtils.isEmpty(route) && StringUtils.isEmpty(uri)){
            return true;
        }
        if (StringUtils.isEmpty(route) || StringUtils.isEmpty(uri)){
            return false;
        }
        String re = route.replaceAll("\\{\\w+\\}","\\(\\\\w+\\)");
        Pattern p = Pattern.compile(re);
        Matcher m = p.matcher(uri);
        boolean ret = m.find();
        return ret;
    }

    /**
     *
     * @param func
     * @param uri 匹配到客户路由
     * @return
     */
    private boolean isFunctionMatch(SysFunction func, String uri){
        if (isRouteMatch(func.getRoute(), uri)){
            return true;
        }
        List<String> dependRoutes = func.getDependRoutes();
        if ( dependRoutes != null) {
            for (String route : dependRoutes){
                if (isRouteMatch(route, uri)){
                    return true;
                }
            }
        }
        return false;
    }
    @Override
    public SysFunction getFunction(String uri) {
        List<SysFunction> functions = getAllFunctions();
        int n = functions.size();

        for (int j = 0; j < n; j++){
            SysFunction func = functions.get(j);
            if (isRouteMatch(func.getRoute(), uri)){
                return func;
            }
        }
        for (int j = 0; j < n; j++){
            SysFunction func = functions.get(j);
            List<String> dependRoutes = func.getDependRoutes();
            if ( dependRoutes == null || dependRoutes.size() == 0) {
                continue;
            }
            for (String route : dependRoutes){
                if (isRouteMatch(route, uri)){
                    return func;
                }
            }
        }
        return null;
    }
    @Override
    public List<SysFunction> getFunctions(String uri) {
        List<SysFunction> ret = new ArrayList<>();
        List<SysFunction> functions = getAllFunctions();
        int n = functions.size();

        for (int j = 0; j < n; j++){
            SysFunction func = functions.get(j);
            if (isRouteMatch(func.getRoute(), uri)){
                ret.add(func);
                continue;
            }
            List<String> dependRoutes = func.getDependRoutes();
            if ( dependRoutes == null || dependRoutes.size() == 0) {
                continue;
            }
            for (String route : dependRoutes){
                if (isRouteMatch(route, uri)){
                    ret.add(func);
                    break;
                }
            }
        }
        return ret;
    }


    @Override
    public UserRole getRole(String key) {
        UserRole role = aclDao.getRoleByKey(key);
        if (role != null){
            role.setFunctions(aclDao.getRoleFunctions(role.getId()));
        }
        return role;
    }

    @Override
    public UserRole getRole(Integer id) {
        UserRole role = aclDao.getRole(id);
        if (role != null){
            role.setFunctions(aclDao.getRoleFunctions(id));
        }
        return role;
    }

    @Override
    public List<UserRole> searchRoles(String name,int offset, int pageSize) {
        return aclDao.searchRoles(name,offset,pageSize);
    }

    @Override
    public int getSearchRolesCount(String name) {
        return aclDao.getSearchRolesCount(name);
    }
    @Override
    public boolean createRole(UserRole role) {
        aclDao.createRole(role);
        boolean ret = role.getId() != null && role.getId()>0;
        if (ret){
            List<Integer>functionIds = role.getFunctionIds();
            if ( ( functionIds != null && functionIds.size() > 0 ) ){
                aclDao.addRoleFunctions(role.getId(),role.getFunctionIds());
            }
        }

        return ret;
    }

    @Override
    public boolean updateRole(UserRole role) {
        aclDao.updateRole(role);
        if (role.getFunctionIds()!=null){
            updateRoleFunctions(role.getId(), role.getFunctionIds());
        }
        return true;
    }
    @Override
    public boolean updateRoleStatus(UserRole role){
        boolean ret = aclDao.updateRoleStatus(role);
        return ret;
    }

    @Override
    public boolean removeRole(int roleId) {
        aclDao.removeRole(roleId);
        aclDao.removeRoleFunctions(roleId);
        return true;
    }

    @Override
    public boolean updateRoleFunctions(int roleId, List<Integer> functionIds) {

        aclDao.removeRoleFunctions(roleId);
        if (functionIds!=null && functionIds.size()>0) {
            aclDao.addRoleFunctions(roleId, functionIds);
        }
        return true;
    }


    @Override
    public List<UserRole> getUserRoles(Long uid) {
        List<UserRole> roles = aclDao.getUserRoles(uid);
        for (UserRole role : roles){
            if (isAdmin(role)){
                role.setAdmin(true);
            }
        }
        return  roles;
    }

    @Override
    public List<UserRole> getUserActiveRoles(Long uid) {
        List<UserRole> activeRoles = new ArrayList<>();
        List<UserRole> roles = getUserRoles(uid);
        if (roles != null){
            for (int n = roles.size(), i = 0; i< n; i++){
                UserRole role = roles.get(i);
                if (isRoleActive(role)){
                    activeRoles.add(role);
                }
            }
        }
        return activeRoles.size() == 0 ? null : activeRoles;
    }

    @Override
    public List<SysFunction> getRoleFunctions(int roleId) {
        List<SysFunction> functions = aclDao.getRoleFunctions(roleId);
        return functions;
    }

    @Override
    public boolean removeUserRoles(Long uid, List<Integer> roles) {
        return aclDao.removeUserRoles(uid, roles);
    }

    @Override
    public boolean removeUserRoles(Long uid) {
        return aclDao.removeAllUserRoles(uid);
    }
    @Override
    public boolean addUserRoles(Long uid, List<UserRole> roles) {
        return aclDao.addUserRoles(uid, roles);
    }

    @Override
    public boolean isAdmin(UserRole role) {
        if ( role != null && "admin".equalsIgnoreCase( role.getKey() ) ){
            return true;
        }
        return false;
    }


    @Override
    public boolean hasRouteRights(List<UserRole> roles, String uri) {
        if (roles == null){
            return false;
        }
        for (int i=0; i<roles.size(); i++){
            UserRole role = roles.get(i);
            if (!isRoleActive(role)){
                continue;
            }
            if (isAdmin(role)){
                return true;
            }
            List<SysFunction> functions = getRoleFunctions(role.getId());
            for (int j = 0; j<functions.size(); j++){
                SysFunction func = functions.get(j);
                if (isFunctionMatch(func, uri)){
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public boolean isRoleActive(UserRole role) {
        boolean ret = role != null && UserRole.STATUS_ACTIVE.equalsIgnoreCase(role.getStatus());
        return ret;
    }

    @Override
    public List<UserRole> getRouteRoles(List<UserRole> roles, String uri){
        if (roles == null) {
            return null;
        }
        List<UserRole> ret = new ArrayList<>();
        for (int i=0; i<roles.size(); i++){
            UserRole role = roles.get(i);
            if (!isRoleActive(role)){
                continue;
            }
            if (isAdmin(role)){
                ret.add(role);
                continue;
            }
            List<SysFunction> functions = getRoleFunctions(role.getId());
            for (int j = 0; j<functions.size(); j++){
                SysFunction func = functions.get(j);
                if (isFunctionMatch(func, uri)){
                    ret.add(role);
                    break;
                }
            }
        }
        return ret;
    }

    @Override
    public List<SysFunction> getUserFunctions(Long uid) {
        List<UserRole> roles = getUserRoles(uid);
        Map menus = new LinkedHashMap();
        for (int i=0; i<roles.size(); i++) {
            List<SysFunction> functions = null;
            UserRole role = roles.get(i);
            if ( !isRoleActive(role) ){
                continue;
            }
            if (isAdmin(role)) {
                functions = getAllFunctions();
            }else{
                functions = getRoleFunctions(role.getId());
            }
            if (functions == null){
                continue;
            }
            for (int j = 0; j<functions.size();j++){
                SysFunction func = functions.get(j);
                boolean flag = false;
                if (!menus.containsKey(func.getId())){
                    menus.put(func.getId(), func);
                }
            }
        }
        return new ArrayList<>(menus.values());
    }

    @Override
    public List<SysFunction> getUserMenus(Long uid) {
        List<SysFunction> functions = getUserFunctions(uid);
        List<SysFunction> menus = new ArrayList<>();
        for (int j = 0; j<functions.size();j++){
            SysFunction func = functions.get(j);
            boolean flag = false;
            if("menu".equals(func.getType())){
                menus.add(func);
            }
        }
        return menus;
    }
}
